let listalticol = ref []


(* Dimensions d'une image *)
let get_dims img =
  ((Sdlvideo.surface_info img).Sdlvideo.w, (Sdlvideo.surface_info img).Sdlvideo.h)
 
(* init de SDL *)
let sdl_init () =
  begin
    Sdl.init [`EVERYTHING];
    Sdlevent.enable_events Sdlevent.all_events_mask;
  end
 
(* attendre une touche ... *)
let rec wait_key () =
  let e = Sdlevent.wait_event () in
    match e with
    Sdlevent.KEYDOWN _ -> ()
      | _ -> wait_key ()


let saveBMP img nom = 
  Sdlvideo.save_BMP img nom(*modifier pour enregistrer l'image apres un traitement*)


 
(*
  show img dst
  affiche la surface img sur la surface de destination dst (normalement l'écran)
*)

let show img dst =
  let d = Sdlvideo.display_format img in
    Sdlvideo.blit_surface d dst ();
    Sdlvideo.flip dst

(*
  affich img
  affichage d'une image et attent une touche pour passer a la suite
*)
let affich img =
  begin
    (* Recuperation de la taille de l'image *)
    let (w,h) = get_dims img in
    (* On ouvre une fenetre en mode doublebuffer *)
    let display = Sdlvideo.set_video_mode w h [`DOUBLEBUF] in
    (* on affiche l'image *)
    show img display;
    (* on attend une touche *)
    wait_key ();
  end


(*
  contourtrace img matris
  Converti tous les points dont la valeur dans la matrice est 1 d'une image en noir retourne l'image modifie
*)

let contourtrace img matris =
  let _ = Sdlvideo.lock img in
  let (w,h) = get_dims img in
    for x = 0 to w - 1 do
      ();
      for y = 0 to h - 1 do
        if matris.(x).(y) = 1 then
          Sdlvideo.put_pixel_color img x y Sdlvideo.black
        else ();
      done;
    done;
   Sdlvideo.unlock img;
  img;;


(*
  load_img nom
  Prends une string en parametre et charge l'image associe sous forme de surface retourne cette surface
*)
let load_img nom = 
  Sdlloader.load_image nom;;


(*
  contour img
  Prends en parametre une surface (charge grace a load_img) et retourne une surface ou les contours ont ete trace
*)
let contour (img) =
    (* Recuperation de la taille de l'image *)
    let (w,h) = get_dims img in
    (* Initialisation de notre tableau *)
    let tab = Array.make_matrix w h 0 in
    (* Parcours de notre image *)
       for y = 0 to h - 1 do
         (); (* Necessaire pour ne pas avoir d'erreur *)
          for x = 0 to w-1 do
               let (r,g,b) = (Sdlvideo.get_pixel_color img x y) in (* On recupere la couleur du pixel courant*) 
               if x<w-1 then (* On verifie que l'on est pas sur la derniere colonne *)
                    let (r1,g1,b1) =  (Sdlvideo.get_pixel_color img (x+1) y) in (* On recupere la couleur du pixel a sa droite *)
                    if (r1,g1,b1) <> (r,g,b) then (* Si ils sont differents ont passe les deux points a 1 *) 
                        let lign = Array.get tab (x+1) in
                        let lign2 = Array.get tab x in
                        let _ = Array.set lign y 1 in
                        let _ = Array.set lign2 y 1 in
                        Array.set tab (x+1) lign
                    else
                        (); (* Sinon on ne fait rien *)
               else ();(* On ne fait rien si on est sur la derniere colonne *)
               if y<h-1 then (* On verifie aue l'on est pas sur la derniere ligne *)
                    let (r1,g1,b1) = (Sdlvideo.get_pixel_color img x (y+1)) in (* On recupere la couleur du pixel au-dessus *)
                    if (r1,g1,b1) <> (r,g,b) then (* Si ils sont differents on les passe tous les deux a 1 *)
                       let lign = Array.get tab x in
                       let _ = Array.set lign (y+1) 1 in
                       let _ = Array.set lign y 1 in
                       Array.set tab x lign
                    else
                       ();(* Si ils sont semblable on ne change rien *)
               else (); (* On ne fait rien si on est sur la derniere ligne *)
          done;
       done;
     let img = contourtrace img tab in (* On trace nos points noirs *)

     (* Partie affichage a modifier pour retourner l'image a la place *)
     img;;




(*
  strictpositif nbr
  Fonction de valeur absolue pour qu'on ne puisse pas passer un interval inferieur ou egale a 0
  retourne le nombre positif correspondant 10 si le chiffre etait 0
*)

let strictpositif = function
    | 0 -> 10
    | x when x < 0 -> -x
    | x -> x;;



(*
  cadrillage img interval
  prend une image en parametre ainsi qu'un interval que l'utilisateur pourra changer
  retourne/affiche l'image obtenue
*)

let cadrillage img interval = 
    let interval = strictpositif (interval) in (* On verifie que notre intervalle est positif et sinon on le change cf. stictpositif*)
    let _ = Sdlvideo.lock img in
    (* Recuperation de la taille de l'image *)
    let (w,h) = get_dims img in
    (* On trace les lignes verticales *)
    for i = 0 to ((w - 1) / interval) do
      let temp2 = i * interval in
      for y = 0 to h - 1 do
        Sdlvideo.put_pixel_color img temp2 y Sdlvideo.black
      done;
    done;

    (* On trace les lignes horizontales *)
    for j = 0 to ((h - 1) / interval) do
      let temp = j * interval in
      for x = 0 to w - 1 do
        Sdlvideo.put_pixel_color img x temp Sdlvideo.black
      done;
    done;
    
    let _ = Sdlvideo.unlock img in
    img;;



let diagonale img interval = 

      let interval = strictpositif (interval) in (* On verifie que notre intervalle est positif et sinon on le change cf. stictpositif*)
      let _ = Sdlvideo.lock img in
	(* Recuperation de la taille de l'image *)
      let (w,h) = get_dims img in
		       				
      let i = ref 0 and j = ref 0 and k = ref 0 in 
	       				
    while (!i<w-1) do 
      while ((!i<w-1)&&(!j<h-1)) do
	Sdlvideo.put_pixel_color img !i !j Sdlvideo.black;
	i:= !i+1;
	j:=!j+1;
      done;
      i := interval + (interval * !k);
      j := 0;
      k := !k +1;
    done; 

    i :=0;
    j :=0;
    k :=0;
    
    while (!j <h-1) do
      while ((!i<w-1)&&(!j<h-1)) do
	Sdlvideo.put_pixel_color img !i !j Sdlvideo.black;
	i:= !i+1;
	j:=!j+1;
      done;
      j := interval + interval * !k;
      i := 0;
      k := !k +1;
    done;

    
    let _ = Sdlvideo.unlock img in
    img;;




let initializSdl () =
  sdl_init ()
  









(* Partie sur la recuperation des points et converti en . obj
***************************************************************)

(* 
  recup img interval
  On parcours notre image avec un interval et on prend tous les points d'intersection, sous la forme d'un triplet (x,y,color) que l'on met dans une liste que l'on renvoie
*)

let recup img interval =
  let interval = strictpositif (interval) in (* On verifie que notre intervalle est positif et sinon on le change cf. stictpositif*)
  (* On initialise un pointeur sur une liste vide*)
  let poinlist = ref [] in
  (* Recuperation de la taille de l'image *)
  let (w,h) = get_dims img in
  (* On recupere les points d'intersections*)
  for i = 0 to ((w - 1) / interval) do
    let x = i * interval in
    for j = 0 to ((h - 1) / interval) do
      let y = j * interval in
      poinlist := (x, y, Sdlvideo.get_pixel_color img x y) :: !poinlist
    done;
  done;
  !poinlist;;
  

(*
  recupMatrix img interval
  Comme recup mais retourne une matrice chaque ligne correspond a une nouvelle rangee horizontale
*)


let recupMatrix img interval =
  let interval = strictpositif (interval) in (* On verifie que notre intervalle est positif et sinon on le change cf. stictpositif*)
  (* Recuperation de la taille de l'image *)
  let (w,h) = get_dims img in
  (* On initialise un pointeur sur une matrice*)
  let poinmatrix = ref (Array.create_matrix ((w-1) / interval)  ((h-1) / interval) (0,0,Sdlvideo.black)) in
  (* On recupere les points d'intersections *)
  for i = 0 to ((w - 1) / interval)-1 do
    let x = i * interval in
    for j = 0 to ((h - 1) / interval)-1 do
      let y = j * interval in
      !poinmatrix.(i).(j) <- (x, y, Sdlvideo.get_pixel_color img x y)
    done;
  done;
  !poinmatrix;;
  
  
(*
  tracepoint img pointlist
  Pour chaque point de notre liste on l'affiche sur notre image et on retourne notre image
*)

let rec tracepoint img pointlist = 
  match pointlist with
    |[] -> img
    |(x, y, couleur) :: l -> let _ = Sdlvideo.put_pixel_color img x y couleur in tracepoint img l;;

(* Fonction de test :
  transblack pointlist
  Pour chaque element de la liste de la forme (x:int, y:int, couleur:color) met couleur a black et retourne la liste
*)
let rec transblack = function
  |[] -> []
  |(x,y,couleur) :: l -> (x,y,Sdlvideo.black) :: transblack l;;


(*
  ecrit str file
  ecrit la string dans le fichier file,
  efface le fichier si il existe deja
*)
let ecrit str file = 
  let out_channel = open_out file in
  output_string out_channel str;
  close_out out_channel;;

(*
  matrixlength mat
  retourne un couple (x, y) contenant la hauteur et la largeur de notre matrice
*)
let matrixlength mat =
  let (x,y) = (Array.length mat, Array.length mat.(0)) in
  (x,y);;

(*
  color_to_altitude (r,g,b)
  prends en parametre une couleur de la forme (r,g,b) et renvoie un int correspondant a la hauteur de notre point
*)
let color_to_altitude (r,g,b) =
  let moy = (r + g + b)/3 in
  moy;;


(*
  ajou_wndoubl x l
  prends un element et une liste si l'element ne fait pas partie de la liste on l'ajoute
*)
let rec ajou_wndoubl (r,g,b) = function
  |[] -> (r,g,b)::[]
  |(r1,g1,b1)::l when r1 = r && g1 = g && b1 = b -> (r1,g1,b1)::l
  |e::l -> e :: ajou_wndoubl (r,g,b) l;;

(*
  list_color img
  prends une image en parametre et retourne la liste des couleurs de cette image
*)
let list_color img = (*interval = 
  let inter = ref strictpositif (interval);*) (* On verifie que notre intervalle est positif et sinon on le change cf. stictpositif*)
  (* initialisation de notre liste de couleur *)
  let li = ref [] in
  (* Recuperation de la taille de l'image *)
  let (w,h) = get_dims img in
  (* On recupere les points d'intersections *)
(*
  for i = 0 to (w / !inter)-1 do
    let x = i * !inter in
    for j = 0 to (h / !inter)-1 do
      let y = j * !inter in
      li := ajou_wndoubl (Sdlvideo.get_pixel_color img x y) !li
    done;
  done;
*)
  for i = 0 to w-1 do
    ();
    for j = 0 to h-1 do
      li := ajou_wndoubl (Sdlvideo.get_pixel_color img i j) !li
    done;
  done;
  !li;;


(*
  ask_alti c
  demande l'altitude a la couleur c
*)
let ask_alti (r,g,b) =
  print_endline("Quel altitude pour la couleur r:" ^ (string_of_int r) ^ " g:" ^ (string_of_int g) ^ " b:" ^ (string_of_int b));
  int_of_string(read_line());;


(*
  altitude l
  prends une liste de couleur en parametre, demande pour chaque couleur l'altitute associe et retourne une liste associant pour chaque couleur son altitude
*)
let rec altitude = function
  |[] -> []
  |(r,g,b)::l -> ((r,g,b), ask_alti (r,g,b)) :: altitude l;;

(*
  altitude_from_img img
  prends une image fait sa liste de couleur demande pour chacune son altitude et retourne la liste de (couleur,altitude)
*)
let altitude_from_img img = 
  altitude (list_color img);;



(*
  search_return_alti c l
  prends une liste contenant d'un cote la couleur d'un cote l'altitude et prend une couleur c retourne l'altitude de la couleur si elle est dans la liste 0 sinon
*)
let rec search_return_alti c = function
  |[] -> 0
  |((r,g,b), a)::l when c = (r,g,b) -> a
  |e::l -> search_return_alti c l;;


(* Tiens compte de la couleur et demande les valeur en console cf ask_alti ou altitude
  pointmatrix_to_posmatrix pointmatrix
  prends en parametre une matrice dt les elements sont de la forme (x, y, color) retour une matrice dont les elements sont de la forme (x, y, z)
*)
let pointmatrix_to_posmatrix pointmatrix img pas = 
  listalticol := altitude_from_img img;
  let l = !listalticol in
  let (x,y) = matrixlength pointmatrix in
  let posmatrix = ref (Array.create_matrix x y (0, 0, 0)) in
  for i = 0 to (x - 1) do
    ();
    for j = 0 to (y - 1) do
      let (w, h, (r,g,b)) = pointmatrix.(i).(j) in
      !posmatrix.(i).(j) <- (w, search_return_alti (r,g,b) l,- h)
    done;
  done;
  !posmatrix;;


(*
  vertices posmatrix
  prend en parametre une matrice dt les elements sont de la forme (x, y, z)
  retourne une string contenant l'ensemble de ces points ecrit sous le format standart de .obj
*)
let vertices posmatrix = 
  let (x,y) = matrixlength posmatrix in
  let str = ref "" in
  for i = 0 to (x - 1) do
    ();
    for j = 0 to (y - 1) do
      let (w, h, z) = posmatrix.(i).(j) in
      str := !str ^ ("v " ^ (string_of_int w) ^ " " ^ (string_of_int h) ^ " " ^ (string_of_int z) ^ "\n")
    done;
  done;
  !str;;



(* 
  faceobj posmatrix
  prend en parametre une matrice dt les elements sont de la forme (x, y, z)
  retourne une string contenant l'ensemble de ces points ecrit sous le format standart de .obj
*)
let faceobj posmatrix = 
  let (x,y) = matrixlength posmatrix in
  let str = ref "" in
  for i = 0 to ((x * y) - (x+2)) do
    if (i+1) mod  x <> 0 then
      let (w, h, z) = (i+1,i+2+x,i+2) in
      str := !str ^ ("f " ^ (string_of_int w) ^ " " ^ (string_of_int h) ^ " " ^ (string_of_int z) ^ "\n");
      let (w, h, z) = (i+1,i+1+x,i+2+x) in
      str := !str ^ ("f " ^ (string_of_int w) ^ " " ^ (string_of_int h) ^ " " ^ (string_of_int z) ^ "\n")
    else
      ()
  done;
  !str;;



(*TICHONOV*)

(* On recupère la couleur de l'image en position x y*)

let readcolor (img,x,y) = 
let (w,h) = get_dims !img in 
if (x<(w) && y<(h))then
  let (r,g,b) =(Sdlvideo.get_pixel_color !img x y) in
(Int32.to_float(Sdlvideo.map_RGB !img (r,g,b)))else 0.



(*on met la couleur en xy*)

let writecolor (img,x,y,couleur) = 
  let coul = Sdlvideo.get_RGB !img couleur in
Sdlvideo.put_pixel_color !img x y coul
 
 
(*dérivé seconde*)

let dxx img x y = (readcolor (img,x+1,y)) -. 2.*. (readcolor (img,x,y)) +. (readcolor (img,x-1,y))

let dyy img x y = (readcolor (img,x,y+1)) -. 2.*. (readcolor (img,x,y)) +. (readcolor (img,x,y-1))


(* algo de reduction de bruit : plusieurs méthodes au choix*)

(*1ère méthode : tikhonov , j'ai mis le nombre d'itérations en paramètre plus on fait d'itération plus l'image devient flou et allonge le temps de l'algo le juste mileu est environ 30*)


let tikhonov img nf  =
  
  let (w,h)=get_dims !img in 
  for n=0 to nf do 
    for y=0 to w-1  do
      for x=0 to h-1 do
	let xx= dxx img x y and yy = dyy img x y in 
	let col =Int32.of_int(int_of_float (0.01*. 5.*. (xx +. yy)+.(readcolor (img,x,y))))in	   
	writecolor (img,x,y,col);          
      done;
    done;
  done;
  !img


let obj img objnom pas =
   let posmatr = (pointmatrix_to_posmatrix (recupMatrix img pas ) img pas ) in
    ecrit ((vertices posmatr)^"\n"^(faceobj posmatr)) objnom
   
let tri l = 
   let rec insert (c,a) = function
       |[] -> [(c,a)]
       |(c2,a2)::l as li when a2>a -> (c,a)::li
       |x::l -> x::(insert (c,a) l) in
     let rec chap l l2 = match l with
       |[] -> l2
       |e::l1 -> chap l1 (insert e l2) in
       chap l []

let ficolor nomfich =
    let rec affcolor = function
      |[] -> ""
      |((r,g,b),a)::l -> (string_of_int r) ^ " " ^ (string_of_int g) ^ " " ^ (string_of_int b) ^ " : " ^ (string_of_int a) ^ "\n" ^ (affcolor l) in
      ecrit (affcolor (tri !listalticol)) nomfich


let sortie img pas objnom colornom = 
   obj img objnom pas;
   ficolor colornom


